import { _decorator, Component, Label, Node, RichText } from 'cc';

import MathExt from '../base/MathExt';

const { ccclass } = _decorator;
/** 格式化函数别名 */
declare type FormatFunc = (v: number) => string;
/** 快速绑定是可能需要的额外参数 */
interface IScrollParam {
    /** 格式化函数 */
    formatFunc?: FormatFunc;
    /** 货币文本显示前缀 */
    prefix?: string;
    /** 初始值 */
    value?: number;
}

// 动画数据对象
class AniStruct {

    // 动画开始值
    private _from: number = 0;
    get from(): number {
        return this._from;
    }
    // 动画目标值
    private _to: number = 0;
    get to(): number {
        return this._to;
    }
    set to(v: number) {
        this._to = v;
    }
    // 动画当前值
    private _current: number = 0;
    get current(): number {
        return this._current;
    }

    constructor(to: number, from?: number, current?: number) {
        this.set(to, from, current);
    }

    set(to: number, from?: number, current?: number) {
        this._to = to;
        this._from = from != undefined ? from : to;
        this._current = current != undefined ? current : this._from;
    }

    get delta(): number {
        return this._to - this._from;
    }

    get needAni(): boolean {
        return this._from != this._to;
    }

    get isEnd(): boolean {
        return this._current == this._to;
    }

    oneStep(delta: number) {
        if (!delta) return;
        const isADD: boolean = this.delta > 0;
        this._current = isADD ? Math.min(this._current + delta, this._to) : Math.max(this._current + delta, this._to);
    }
}

/**
 * 处理数值文本渐变动画效果
 * 支持 Label | RichText
 */
@ccclass('ScrollLabel')
export default class ScrollLabel extends Component {

    /** 需要动画的文本组件 */
    private get label() {
        if (!this._label) {
            this._label = this.node.getComponent(Label) || this.node.getComponent(RichText);
        }
        if (!this._label) {
            console.error('# if you use ScrollLabel, you need to append Label Component');
            return;
        }
        return this._label;
    }
    private _label: Label | RichText;

    /** 更新文本内容 */
    private _setLabel() {
        if (!this._aniData)
            return;
        this.label.string = this.formatedStr;
    }

    /**
     * 给文本节点绑定一个数值动画控件
     * @param label 文本节点，支持 Label | RichText
     * @param param IScrollParam 一些附加可选参数
     * @returns ScrollLabel
     */
    static create(label: Node | Label | RichText, param?: IScrollParam): ScrollLabel {
        if (!label || !label.isValid)
            return;
        if (!(label instanceof Node))
            label = label.node;
        // comp
        let comp = label.getComponent(ScrollLabel);
        if (!comp)
            comp = label.addComponent(ScrollLabel);
        // format func
        if (param?.formatFunc)
            comp.formatFunc = param.formatFunc;
        // prefix
        if (!!param?.prefix)
            comp.prefix = param.prefix;
        // value
        if (param?.value != undefined)
            comp.value = param.value;
        return comp;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////

    /** 预先设置格式化字符串函数 */
    public set formatFunc(func: FormatFunc) {
        this._formatFunc = func;
    }
    private _formatFunc: FormatFunc;

    /** 获取格式化后的数值字符串 */
    private get formatedStr(): string {
        let str = "";
        if (this._formatFunc) {
            str = this._formatFunc(this.value);
        }
        else {
            str = this.value.toString();
        }
        return this.prefix + str;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////

    // 计时器ID
    private timerID: number = -1;
    private clearTimer() {
        if (this.timerID >= 0) {
            clearInterval(this.timerID);
            this.timerID = -1;
        }
    }
    private startTimer() {
        this.clearTimer();
        if (!this._scroll || !this._aniData || !this._aniData.needAni)
            return;
        const space: number = 10;
        const slice: number = this._aniData.delta * space / (this._duration * 1000);
        this.timerID = setInterval(() => {
            if (!this || !this.isValid)
                return;
            let random = 0;
            if (Math.abs(slice) > 10)
                random = MathExt.int(10, -10);
            this._aniData.oneStep(slice + random);
            this._setLabel();
            if (this._aniData.isEnd) {
                this.clearTimer();
                this._aniData.set(this._aniData.to);
            }
        }, space);
    }

    /** 是否滚动 */
    set scroll(v: boolean) {
        this._scroll = v;
        this.clearTimer();
        if (!this._scroll)
            return;

        if (!!this.valueTemp) {
            this._aniData.to = this.valueTemp + this._aniData.from;
        }
        this.startTimer();
    }
    get scroll() {
        return this._scroll;
    }
    private _scroll: boolean = true;

    ////////////////////////////////////////////////////////////////////////////////////////////////////

    /** 当前值，直接赋值会立即刷新当前值，不会进行滚动 */
    set value(v: number) {
        this.valueTemp = 0;
        if (!this._aniData) {
            this._aniData = new AniStruct(v);
        } else {
            this.clearTimer();
            this._aniData.set(v);
        }
        this._setLabel();
    }
    get value(): number {
        return this._aniData.current;
    }
    private _aniData: AniStruct = null;

    ////////////////////////////////////////////////////////////////////////////////////////////////////

    /** 货币文本显示前缀 */
    public prefix: string = '';

    /** 中途修改变化值用 */
    public valueTemp: number = 0;

    /** 滚动动画持续时间 */
    private _duration: number = 0;


    protected onLoad() {
        try {
            const v = parseInt(this.label.string) || 0;
            this.value = v;
        } catch {
            this.value = 0;
        }
    }

    protected onDestroy() {
        this.valueTemp = 0;
        this.clearTimer();
    }

    /**
     * 滚动到目标值
     * @param duration 滚动总时长(默认1s)
     * @param value 目标值
     */
    public scrollTo(value: number, duration: number = 1) {
        this._aniData.to = value;
        this._duration = duration;

        if (this._scroll && this._aniData.needAni) {
            this.clearTimer();
            this.startTimer();
        }
    }

}

